/*
 * toolkit.c
 *
 *  Created on: 2011-6-1
 *      Author: andy
 */
#include "toolkit.h"
#include "public.h"
#include "types.h"
#include "pcap.h"

#include "ethernet.h"
#include "ether_arp.h"

sstring ToolKit::getAllEtherDevice(std::vector<EtherDevice>& ether_devices)
{
	char errbuf[PCAP_ERRBUF_SIZE];
	pcap_if_t *alldevs = NULL;

	/* get all device */
	if (pcap_findalldevs(&alldevs, errbuf) == -1)
	{
		return sstring("Error in pcap_findalldevs: ") + errbuf;
	}
	if (alldevs == NULL)
	{
		return "No interfaces found! Make sure WinPcap is installed.";
	}

	pcap_if_t *device = NULL;
	for (device = alldevs; device; device = device->next)
	{
		EtherDevice ether_device;
		ether_device.name = device->name;
		if (device->description)
		{
			ether_device.desc = device->description;
		}
		pcap_addr_t *local_address;
		//get ip address and netmask
		for (local_address = device->addresses; local_address != NULL; local_address = local_address->next)
		{
			if (local_address->addr->sa_family != AF_INET)
			{
				continue;
			}
			ether_device.ip = (((struct sockaddr_in *) (local_address->addr))->sin_addr.s_addr);
			ether_device.netmask = (((struct sockaddr_in *) (local_address->netmask))->sin_addr.s_addr);
			#ifdef WIN32
				int ret = getMacAddress(inet_ntoa(*((struct in_addr*) &ether_device.ip)), ether_device.mac_addr);
			#else
				int ret = getMacAddress(ether_device.name.c_str(), ether_device.mac_addr);
			#endif
			if(-1 != ret)
			{
				ether_devices.push_back(ether_device);
			}
			break;
		}	
	}

	pcap_freealldevs(alldevs);

	return "";
}
#ifdef WIN32
int ToolKit::getMacAddress(const char* ip, u_char* mac)
{
	IP_ADAPTER_INFO * info = NULL;
	IP_ADAPTER_INFO * pos = NULL;
	DWORD size = 0;

	GetAdaptersInfo(info, &size);

	info = (IP_ADAPTER_INFO *) malloc(size);

	GetAdaptersInfo(info, &size);

	for (pos = info; pos != NULL; pos = pos->Next)
	{
		IP_ADDR_STRING *ip_addr = &pos->IpAddressList;
		do
		{
			if (strcmp(ip, ip_addr->IpAddress.String) == 0)
			{
				memcpy(mac, pos->Address, 6);
				free(info);
				return 0;
			}
		} while (ip_addr->Next != NULL);
	}

	free(info);

	return -1;
}
#else
#include <sys/ioctl.h>
int ToolKit::getMacAddress(const char* name, u_char* mac)
{
	int sock = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    	if (sock == -1)
    	{
        	return -1;
    	}
	
	struct ifreq ifr;
	strcpy (ifr.ifr_name , name);
    	int status = ioctl (sock, SIOCGIFHWADDR, &ifr);
    	if (status < 0)
    	{
        	close (sock);
        	return -2;
    	}

    	memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LEN);

        return 0;
}
#endif
sstring ToolKit::toMacString(const u_int8* mac)
{
	char mac_addr[MAC_STR_LEN];

	sprintf(mac_addr, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x"
		, (u_int16) mac[0]
		, (u_int16) mac[1]
		, (u_int16) mac[2]
		, (u_int16) mac[3]
		, (u_int16) mac[4]
		, (u_int16) mac[5]);

	return mac_addr;
}

sstring ToolKit::sendArp(pcap_t *adhandle, const EtherDevice& device, u_int32 dst_ip)
{
	struct EtherHeader ethernet_head;
	struct EtherArpHeader arp;
	u_int8 buffer[64];//14 + 28 + 18 + 4
	u_int32 dip = htonl(dst_ip);
	u_int32 sip = device.ip;

	memset(ethernet_head.daddr, 0xFF, sizeof(ethernet_head.daddr));
	memcpy(ethernet_head.saddr, device.mac_addr, 6);
	ethernet_head.type = htons(ETHERNET_ARP);

	arp.ar_hrd = htons(ARPHRD_ETHER);
	arp.ar_pro = htons(ETHERNET_IP); /* Format of protocol address.  */
	arp.ar_hln = 6; /* Length of hardware address.  */
	arp.ar_pln = 4; /* Length of protocol address.  */
	arp.ar_op = htons(ARPOP_REQUEST); /* ARP opcode (command).  */

	memcpy(arp.arp_sha, device.mac_addr, 6);
	memcpy(arp.arp_spa, (u_int8*) &sip, 4); /* sender protocol address */
	memcpy(arp.arp_tpa, (u_int8*) &dip, 4); /* target protocol address */

	memset(buffer, '\0', sizeof(buffer));
	memcpy(buffer, &ethernet_head, 14);
	memcpy(buffer + 14, &arp, 28);

	if (pcap_sendpacket(adhandle, buffer, sizeof(buffer)) != 0)
	{
		return sstring("Error sending the packet: ") + pcap_geterr(adhandle);
	}

	return "";
}



sstring ToolKit::getHostName(u_int32 ip)
{
	struct hostent *remoteHost;
	remoteHost = gethostbyaddr((char *) &ip, 4, AF_INET);
	if (remoteHost == NULL)
	{
		return "";
	}
	return remoteHost->h_name;
}
